package database

import (
	"database/sql"
	"reflect"
	"fmt"
	"strconv"
	"strings"
)

// map row data to string
func rowMapper(rows *sql.Rows) ([]map[string]string, error) {
	cols, e := rows.Columns()
	if e != nil {
		return nil, e
	}
	tmpItf := make([]interface {}, len(cols))
	for i, _ := range tmpItf {
		tmpItf[i] = new(string)
	}
	// set returning result
	rs := make([]map[string]string, 0)
	for rows.Next() {
		rows.Scan(tmpItf...)
		rowRst := make(map[string]string)
		// make all column values to string and append to result
		for i, col := range tmpItf {
			rowRst[cols[i]] = fmt.Sprint(reflect.Indirect(reflect.ValueOf(col)).Interface())
		}
		rs = append(rs, rowRst)
	}
	return rs, nil
}

// parse sql.Result to int64
func resultParser(rs sql.Result) (int64, error) {
	i, e := rs.LastInsertId()
	if e != nil {
		return -1, e
	}
	if i < 1 {
		i, e = rs.RowsAffected()
		if e != nil {
			return -1, e
		}
	}
	return i, nil
}

// row struct field
type rowStruct struct {
	Column string
	Type   string
	Field  string
}

// parse map[string]string row to struct
func rowMapStruct(rt reflect.Type, row map[string]string, rs map[string]rowStruct) (rv reflect.Value) {
	rv = reflect.New(rt)
	for k, v := range row {
		t := rs[k]
		if t.Field != "" {
			switch t.Type{
			case "int", "int32", "int64":
				i, _ := strconv.ParseInt(v, 10, 64)
				rv.Elem().FieldByName(t.Field).SetInt(i)
			case "float", "float32", "float64":
				f, _ := strconv.ParseFloat(v, 64)
				rv.Elem().FieldByName(t.Field).SetFloat(f)
			case "bool":
				b, _ := strconv.ParseBool(v)
				rv.Elem().FieldByName(t.Field).SetBool(b)
			case "string":
				rv.Elem().FieldByName(t.Field).SetString(v)
			default:
				rv.Elem().FieldByName(t.Field).SetString(v)
			}
		}
	}
	return
}

// Map reflect.Type of struct to rowStruct map information
func mapStruct(rt reflect.Type) map[string]rowStruct {
	n := rt.NumField()
	m := map[string]rowStruct{}
	for i := 0; i < n; i++ {
		rf := rt.Field(i)
		tag := rf.Tag
		if tag.Get("hx") == "-" {
			continue
		}
		// save as row struct
		// column_name is lower spelling fo field_name
		r := rowStruct{}
		r.Column = strings.ToLower(rf.Name)
		r.Type = rf.Type.Name()
		r.Field = rf.Name
		m[r.Column] = r
	}
	return m
}

